<!DOCTYPE html>
<html>
    
<head>
    <meta charset="utf-8">
    
    <title>Java反射真的很慢吗 | ApocalypseBlog</title>
    <canvas id="header_canvas"style="position:absolute;bottom:0"></canvas> 
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

    

    

    

    

    
<link rel="stylesheet" href="/dist/build.css?v=1.14.0.css">

    <script src="/javascripts/bubble.js"></script>
    <script>
        window.isPost = true
        window.aomori = {
            
            
        }
        window.aomori_logo_typed_animated = false
        window.aomori_search_algolia = false

    </script>
<script>
    ((window.gitter = {}).chat = {}).options = {
      room: 'ApocalypseBlog/Apocalypse'
    };
  </script>
  <script src="https://sidecar.gitter.im/dist/sidecar.v1.js" async defer></script>
<meta name="generator" content="Hexo 5.3.0"></head>
<!--DaoVoice服务http://blog.daovoice.io/-->

    <script>(function (i, s, o, g, r, a, m) {
        i['DaoVoiceObject'] = r;
        i[r] = i[r] ||
          function () {
            (i[r].q = i[r].q || []).push(arguments);
          };
        i[r].l = 1 * new Date();
        a = s.createElement(o);
        m = s.getElementsByTagName(o)[0];
        a.async = 1;
        a.src = g;
        a.charset = 'utf-8';
        m.parentNode.insertBefore(a, m);
      })(window, document, 'script', ('https:' === document.location.protocol ? 'https:' : 'http:') + "//widget.daovoice.io/widget/b00f5052.js", 'daovoice');
      daovoice('init', {
        app_id: "b00f5052",
      });
      daovoice('update');
    </script>
  
<body>

    <div class="container">
    <header class="header">
        <div class="header-type">
            
            <div class="header-type-inner">
                
                    <a class="header-type-title" href="/">ApocalypseBlog</a>
                
    
                
            </div>
        </div>
        <div class="header-menu">
            <div class="header-menu-inner">
                
            </div>
            <div class="header-menu-social">
                
            </div>
        </div>

        <div class="header-menu-mobile">
            <div class="header-menu-mobile-inner" id="mobile-menu-open">
                <i class="icon icon-menu"></i>
            </div>
        </div>
    </header>

    <div class="header-menu-mobile-menu">
        <div class="header-menu-mobile-menu-bg"></div>
        <div class="header-menu-mobile-menu-wrap">
            <div class="header-menu-mobile-menu-inner">
                <div class="header-menu-mobile-menu-close" id="mobile-menu-close">
                    <i class="icon icon-cross"></i>
                </div>
                <div class="header-menu-mobile-menu-list">
                    
                </div>
            </div>
        </div>
    </div>

</div>

    <div class="container">
        <div class="main">
            <section class="inner">
                <section class="inner-main">
                    <div class="post">
    <article id="post-ckk6o6avv000x7kny948g161u" class="article article-type-post" itemscope
    itemprop="blogPost">

    <div class="article-inner">

        
          
        
        
        

        
        <header class="article-header">
            
  
    <h1 class="article-title" itemprop="name">
      Java反射真的很慢吗
    </h1>
  

        </header>
        

        <div class="article-more-info article-more-info-post hairline">

            <div class="article-date">
  <time datetime="2020-06-11T21:49:50.000Z" itemprop="datePublished">2020-06-12</time>
</div>

            

            

            

        </div>

        <div class="article-entry post-inner-html hairline" itemprop="articleBody">
            <h2 id="反射到底是好是坏"><a href="#反射到底是好是坏" class="headerlink" title="反射到底是好是坏"></a>反射到底是好是坏</h2><p>说到Java 中的反射，初学者在刚刚接触到反射的各种高级特性时，往往表示十分兴奋，甚至会在一些不需要使用反射的场景中强行使用反射来「炫技」。而经验较为丰富的长者，看到反射时往往会发出灵魂三问：为什么要用反射？反射不会降低性能么？不用还有什么办法可以解决这个问题？</p>
<a id="more"></a>

<p>那么今天我们就来深入探讨下，反射到底对性能有多大影响？顺便探讨下，反射为什么对性能有影响？</p>
<h2 id="编码试验"><a href="#编码试验" class="headerlink" title="编码试验"></a>编码试验</h2><p>在我们分析具体原理之前，我们可以通过编写代码做实验得出结论。</p>
<p>反射可能会涉及多种类型的操作，比如生成实例，获取/设置变量属性，调用方法等。经过简单的思考，我们认为生成实例对性能的影响相对其他操作要大一些，所以我们采用生成实例来做试验。</p>
<p>在如下代码中，我们定义了一个类 <code>InnerClass</code>，我们测试分别使用<code>new</code>和<code>反射</code>来生成 <code>MAX_TIMES</code>个实例，并打印出耗时时间。</p>
<pre><code>public class MainActivity extends AppCompatActivity &#123;

    private static final String TAG = &quot;MainAc&quot;;
    private final int MAX_TIMES = 100 * 1000;
    private InnerClass innerList[];

    @Override
    protected void onCreate(Bundle savedInstanceState) &#123;
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        innerList = new InnerClass[MAX_TIMES];

        long startTime = SystemClock.elapsedRealtime();
        for (int i=0; i &lt; MAX_TIMES; i++) &#123;
            innerList[i] = new InnerClass();
        &#125;
        Log.e(TAG, &quot;totalTime: &quot; + (SystemClock.elapsedRealtime() - startTime));

        long startTime2 = SystemClock.elapsedRealtime();
        for (int i=0; i &lt; MAX_TIMES; i++) &#123;
            innerList[i] = newInstanceByReflection();
        &#125;
        Log.e(TAG, &quot;totalTime2: &quot; + (SystemClock.elapsedRealtime() - startTime2));
    &#125;

    public InnerClass newInstanceByReflection() &#123;
        Class clazz = InnerClass.class;
        try &#123;
            return (InnerClass) clazz.getDeclaredConstructor().newInstance();
        &#125; catch (NoSuchMethodException e) &#123;
            e.printStackTrace();
        &#125; catch (IllegalAccessException e) &#123;
            e.printStackTrace();
        &#125; catch (InstantiationException e) &#123;
            e.printStackTrace();
        &#125; catch (InvocationTargetException e) &#123;
            e.printStackTrace();
        &#125;
        return null;
    &#125;

    static class InnerClass &#123;
    &#125;
&#125;
</code></pre>
<p>输出日志：</p>
<pre><code>2020-03-19 22:34:49.738 2151-2151/? E/MainAc: totalTime: 15
2020-03-19 22:34:50.409 2151-2151/? E/MainAc: totalTime2: 670
</code></pre>
<p>使用反射生成 10万 个实例，耗时 670ms，明显高于直接使用 <code>new</code>关键字的 15ms，所以反射性能低。别急，这个结论总结的还有点早，我们将要生成的实例总数改为 1000个试试，输出日志：</p>
<pre><code>2020-03-19 22:39:21.287 3641-3641/com.example.myapplication E/MainAc: totalTime: 2
2020-03-19 22:39:21.296 3641-3641/com.example.myapplication E/MainAc: totalTime2: 9
</code></pre>
<p>使用反射生成 1000 个实例，虽然需要9ms，高于<code>new</code>的 2ms，但是 9ms 和 2ms 的差距本身肉眼不可见，而且通常我们在业务中写的反射一般来说执行频率也未必会超过 1000 次，这种场景下，我们还能理直气壮地说反射性能很低么？</p>
<p>很显然，不能。</p>
<p>除了代码执行耗时，我们再看看反射对内存的影响。我们仍然以生成 10万 个实例为目标，对上述代码做略微改动，依次只保留 <code>new</code> 方式和反射方式，然后运行程序，观察内存占用情况。</p>
<p><img src="http://user-gold-cdn.xitu.io/2020/3/20/170f7df2febd3611?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="img"></p>
<p>使用 <code>new</code> 方式</p>
<p><img src="http://user-gold-cdn.xitu.io/2020/3/20/170f7df300d4decd?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="img"></p>
<p>使用反射</p>
<p>对比两图，我们可以看到第二张图中多了很多 <code>Constructor</code>和<code>Class</code>对象实例，这两部分占用的内存2.7M。因此，我们可以得出结论，反射会产生大量的临时对象，并且会占用额外内存空间。</p>
<h2 id="刨根问底：反射原理是什么"><a href="#刨根问底：反射原理是什么" class="headerlink" title="刨根问底：反射原理是什么"></a>刨根问底：反射原理是什么</h2><p>我们以前面试验中反射生成实例的代码为入口。</p>
<p>首先回顾下虚拟机中类的生命周期：加载，连接（验证，准备，解析），初始化，使用，卸载。在加载的过程 中，虚拟机会把类的字节码转换成运行时数据结构，并保存在方法区，在内存中会生成一个代表这个类数据结构的 java.lang.Class 对象，后续访问这个类的数据结构就可以通过这个 Class 对象来访问。</p>
<pre><code>public InnerClass newInstanceByReflection() &#123;
    // 获取虚拟机中 InnerClass 类的 Class 对象
    Class clazz = InnerClass.class;
    try &#123;
        return (InnerClass) clazz.getDeclaredConstructor().newInstance();
    &#125; catch (NoSuchMethodException e) &#123;
        e.printStackTrace();
    &#125; catch (IllegalAccessException e) &#123;
        e.printStackTrace();
    &#125; catch (InstantiationException e) &#123;
        e.printStackTrace();
    &#125; catch (InvocationTargetException e) &#123;
        e.printStackTrace();
    &#125;
    return null;
&#125;
</code></pre>
<p>代码中 <code>clazz.getDeclaredConstructor()</code> 用于获取类中定义的构造方法，由于我们没有显式定义构造方法，所以会返回编译器为我们自己生成的默认无参构造方法。</p>
<p>下面我们看下 <code>getDeclaredConstructor</code>是如何返回构造方法的。以下均以 jdk 1.8代码为源码。</p>
<pre><code>@CallerSensitive
public Constructor&lt;T&gt; getDeclaredConstructor(Class&lt;?&gt;... parameterTypes)
    throws NoSuchMethodException, SecurityException &#123;
    // 权限检查
    checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
    return getConstructor0(parameterTypes, Member.DECLARED);
&#125;
</code></pre>
<p><code>getDeclaredConstructor</code> 方法首先做了权限检查，然后直接调用 <code>getConstructor0</code> 方法。</p>
<pre><code>private Constructor&lt;T&gt; getConstructor0(Class&lt;?&gt;[] parameterTypes,
                                    int which) throws NoSuchMethodException
&#123;
    // privateGetDeclaredConstructors 方法是获取所有的构造方法数组
    Constructor&lt;T&gt;[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
    // 遍历所有的构造方法数组，根据传入的参数类型依次匹配，找到合适的构造方法后就会拷贝一份作为返回值
    for (Constructor&lt;T&gt; constructor : constructors) &#123;
        if (arrayContentsEq(parameterTypes,
                            constructor.getParameterTypes())) &#123;
            // 拷贝构造方法
            return getReflectionFactory().copyConstructor(constructor);
        &#125;
    &#125;
    // 没有找到的话，就抛出异常 
    throw new NoSuchMethodException(getName() + &quot;.&lt;init&gt;&quot; + argumentTypesToString(parameterTypes));
&#125;
</code></pre>
<p><code>getConstructor0</code> 方法主要做了两件事：</p>
<ul>
<li>获取所有构造方法组成的数组</li>
<li>遍历构造方法数组，找到匹配的</li>
</ul>
<p>遍历匹配没啥好说的，我们重点看下第一件事，怎么获取的所有构造方法数组，也就是这个方法 <code>privateGetDeclaredConstructors</code>。</p>
<pre><code>private Constructor&lt;T&gt;[] privateGetDeclaredConstructors(boolean publicOnly) &#123;
    checkInitted();
    Constructor&lt;T&gt;[] res;
    // 获取缓存的 ReflectionData 数据
    ReflectionData&lt;T&gt; rd = reflectionData();
    // 如果缓存中有 ReflectionData，就先看看 ReflectionData 中的 publicConstructors 或 declaredConstructors是否为空
    if (rd != null) &#123;
        res = publicOnly ? rd.publicConstructors : rd.declaredConstructors;
        if (res != null) return res;
    &#125;
    // 如果没有缓存，或者缓存中构造方法数组为空
    // No cached value available; request value from VM
    // 对接口类型的字节码特殊处理
    if (isInterface()) &#123;
        @SuppressWarnings(&quot;unchecked&quot;)
        // 如果是接口类型，那么生成一个长度为0的构造方法数组
        Constructor&lt;T&gt;[] temporaryRes = (Constructor&lt;T&gt;[]) new Constructor&lt;?&gt;[0];
        res = temporaryRes;
    &#125; else &#123;
        // 如果不是接口类型，就调用 getDeclaredConstructors0 获取构造方法数组
        res = getDeclaredConstructors0(publicOnly);
    &#125;
    // 获取到构造方法数组后，再赋值给缓存 ReflectionData 中的对应属性
    if (rd != null) &#123;
        if (publicOnly) &#123;
            rd.publicConstructors = res;
        &#125; else &#123;
            rd.declaredConstructors = res;
        &#125;
    &#125;
    return res;
&#125;
</code></pre>
<p>上述代码中我已经对关键代码进行了注释，在讲解整个流程之前，我们看到了一个陌生的类型 <code>ReflectionData</code>。它对应的数据结构是：</p>
<pre><code>private static class ReflectionData&lt;T&gt; &#123;
    volatile Field[] declaredFields;
    volatile Field[] publicFields;
    volatile Method[] declaredMethods;
    volatile Method[] publicMethods;
    volatile Constructor&lt;T&gt;[] declaredConstructors;
    volatile Constructor&lt;T&gt;[] publicConstructors;
    // Intermediate results for getFields and getMethods
    volatile Field[] declaredPublicFields;
    volatile Method[] declaredPublicMethods;
    volatile Class&lt;?&gt;[] interfaces;

    // Value of classRedefinedCount when we created this ReflectionData instance
    final int redefinedCount;

    ReflectionData(int redefinedCount) &#123;
        this.redefinedCount = redefinedCount;
    &#125;
&#125;
</code></pre>
<p><code>ReflectionData</code> 这个类就是用来保存从虚拟机中获取到的一些数据。同时我们可以看到所有反射属性都使用了 <code>volatile</code>关键字修饰。</p>
<p>获取缓存的 <code>ReflectionData</code> 数据是通过调用<code>reflectionData()</code>方法获取的。</p>
<pre><code>// 定义在 Class 类中的反射缓存对象
private volatile transient SoftReference&lt;ReflectionData&lt;T&gt;&gt; reflectionData;

private ReflectionData&lt;T&gt; reflectionData() &#123;
    SoftReference&lt;ReflectionData&lt;T&gt;&gt; reflectionData = this.reflectionData;
    int classRedefinedCount = this.classRedefinedCount;
    ReflectionData&lt;T&gt; rd;
    if (useCaches &amp;&amp;
        reflectionData != null &amp;&amp;
        (rd = reflectionData.get()) != null &amp;&amp;
        rd.redefinedCount == classRedefinedCount) &#123;
        return rd;
    &#125;
    // else no SoftReference or cleared SoftReference or stale ReflectionData
    // -&gt; create and replace new instance
    return newReflectionData(reflectionData, classRedefinedCount);
&#125;
</code></pre>
<p>我们可以看到 <code>reflectionData</code>实际上是一个软引用，软引用会在内存不足的情况下被虚拟机回收，所以<code>reflectionData()</code>方法在开始的地方，先判断了是否可以使用缓存以及缓存是否失效，如果失效了，就会调用 <code>newReflectionData</code>方法生成一个新的 <code>ReflectionData</code> 实例。</p>
<p>接下来看看 <code>newReflectionData</code> 方法。</p>
<pre><code>private ReflectionData&lt;T&gt; newReflectionData(SoftReference&lt;ReflectionData&lt;T&gt;&gt; oldReflectionData,
                                                int classRedefinedCount) &#123;
    // 如果不允许使用缓存，直接返回 null
    if (!useCaches) return null;

    while (true) &#123;
        ReflectionData&lt;T&gt; rd = new ReflectionData&lt;&gt;(classRedefinedCount);
        // try to CAS it...
        if (Atomic.casReflectionData(this, oldReflectionData, new SoftReference&lt;&gt;(rd))) &#123;
            return rd;
        &#125;
        // else retry
        oldReflectionData = this.reflectionData;
        classRedefinedCount = this.classRedefinedCount;
        if (oldReflectionData != null &amp;&amp;
            (rd = oldReflectionData.get()) != null &amp;&amp;
            rd.redefinedCount == classRedefinedCount) &#123;
            return rd;
        &#125;
    &#125;
&#125;
</code></pre>
<p><code>newReflectionData</code>中使用 <strong>volatile + 死循环 + CAS 机制</strong> 保证线程安全。注意到这里的死循环每执行一次都会构造一个新的 <code>ReflectionData</code> 实例。</p>
<p>你可能会有疑问，<code>Class</code> 中 <code>reflectionData</code>属性什么时候被赋值的，其实是封装在<code>Atomic.casReflectionData</code>这个方法里了，他会检测当前<code>Class</code>对象中的<code>reflectionData</code>是否与<code>oldReflectionData</code>相等，如果相等，就会把<code>new SoftReference&lt;&gt;(rd)</code>赋值给 <code>reflectionData</code>。</p>
<p>到现在为止，关于 <code>ReflectionData</code>的背景知识都介绍完了。我们再回到 <code>privateGetDeclaredConstructors</code>中看看获取构造方法的流程。</p>
<p><img src="http://user-gold-cdn.xitu.io/2020/3/20/170f7df304157365?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="img"></p>
<p><code>privateGetDeclaredConstructors</code>流程图</p>
<p>可以看到对于普通类，最终通过调用 <code>getDeclaredConstructors0</code>方法获取的构造方法列表。</p>
<pre><code>private native Constructor&lt;T&gt;[] getDeclaredConstructors0(boolean publicOnly);
</code></pre>
<p>这个方法是 native 的，具体逻辑在 jdk 源码中。</p>
<p>在 <code>native/java/lang/Class_getDeclaredConstructors0.c</code> 文件中，</p>
<pre><code>void getDeclaredConstructors0(Frame * frame)
&#123;
    // Frame 可以理解为调用native方法时，java层传递过来的数据的一种封装
    LocalVars * vars = frame-&gt;localVars;
    Object * classObj = getLocalVarsThis(vars);
    // 取得java方法的入参
    bool publicOnly = getLocalVarsBoolean(vars, 1);

    uint16_t constructorsCount = 0;
    // 获取要查询的类的 Class 对象
    Class * c = classObj-&gt;extra;
    // 获取这个类的所有构造方法，且数量保存在 constructorsCount 中
    Method* * constructors = getClassConstructors(c, publicOnly, &amp;constructorsCount);
    // 获取 java 方法调用所属的 classLoader
    ClassLoader *  classLoader = frame-&gt;method-&gt;classMember.attachClass-&gt;classLoader;
    // 拿到 Constructor 对应的 class 对象
    Class * constructorClass = loadClass(classLoader, &quot;java/lang/reflect/Constructor&quot;);
    //创建一个长度为 constructorsCount 的数组保存构造方法
    Object * constructorArr = newArray(arrayClass(constructorClass), constructorsCount);

    pushOperandRef(frame-&gt;operandStack, constructorArr);
    // 后面是具体的赋值逻辑。将native中的Method对象转化为java层的Constructor对象
    if (constructorsCount &gt; 0)
    &#123;
        Thread * thread = frame-&gt;thread;
        Object* * constructorObjs = getObjectRefs(constructorArr);

        Method * constructorInitMethod = getClassConstructor(constructorClass, _constructorConstructorDescriptor);
        for (uint16_t i = 0; i &lt; constructorsCount; i++)
        &#123;
            Method * constructor = constructors[i];

            Object * constructorObj = newObject(constructorClass);
            constructorObj-&gt;extra = constructor;
            constructorObjs[i] = constructorObj;

            OperandStack * ops = newOperandStack(9);
            pushOperandRef(ops, constructorObj);
            pushOperandRef(ops, classObj);
            pushOperandRef(ops, toClassArr(classLoader, methodParameterTypes(constructor), constructor-&gt;parsedDescriptor-&gt;parameterTypesCount));
            if (constructor-&gt;exceptions != NULL)
                pushOperandRef(ops, toClassArr(classLoader, methodExceptionTypes(constructor), constructor-&gt;exceptions-&gt;number_of_exceptions));
            else
                pushOperandRef(ops, toClassArr(classLoader, methodExceptionTypes(constructor), 0));
            pushOperandInt(ops, constructor-&gt;classMember.accessFlags);
            pushOperandInt(ops, 0);
            pushOperandRef(ops, getSignatureStr(classLoader, constructor-&gt;classMember.signature));         // signature
            pushOperandRef(ops, toByteArr(classLoader, constructor-&gt;classMember.annotationData, constructor-&gt;classMember.annotationDataLen));
            pushOperandRef(ops, toByteArr(classLoader, constructor-&gt;parameterAnnotationData, constructor-&gt;parameterAnnotationDataLen));


            Frame * shimFrame = newShimFrame(thread, ops);
            pushThreadFrame(thread, shimFrame);

            // init constructorObj
            InvokeMethod(shimFrame, constructorInitMethod);
        &#125;


    &#125;
&#125;
</code></pre>
<p>从上面的逻辑，可以知道获取构造方法的核心方法是 <code>getClassConstructors</code> ，所在文件为 <code>rtda/heap/class.c</code>。</p>
<pre><code>Method* * getClassConstructors(Class * self, bool publicOnly, uint16_t * constructorsCount)
&#123;
    // 分配大小为 sizeof(Method) 的长度为 methodsCount 的连续内存地址，即数组
    Method* * constructors = calloc(self-&gt;methodsCount, sizeof(Method));
    *constructorsCount = 0;
    // 在native 层，构造方法和普通方法都存在 methods 中，逐一遍历
    for (uint16_t i = 0; i &lt; self-&gt;methodsCount; i++)
    &#123;
        Method * method = self-&gt;methods + i;
        // 判断是否是构造方法
        if (isMethodConstructor(method))
        &#123;
            // 检查权限
            if (!publicOnly || isMethodPublic(method))
            &#123;
                // 符合条件的构造方法依次存到数组中
                constructors[*constructorsCount] = method;
                (*constructorsCount)++;
            &#125;
        &#125;
    &#125;
    return constructors;
&#125;
</code></pre>
<p>可以看到<code>getClassConstructors</code>实际上就是对 <code>methods</code> 进行了一次过滤，过滤的条件为：1.是构造方法；2.权限一致。</p>
<p><code>isMethodConstructor</code> 方法的判断逻辑也是十分简单，不是静态方法，而且方法名是<code>&lt;init&gt;</code>即可。</p>
<pre><code>bool isMethodConstructor(Method * self)
&#123;
    return !isMethodStatic(self) &amp;&amp; strcmp(self-&gt;classMember.name, &quot;&lt;init&gt;&quot;) == 0;    
&#125;
</code></pre>
<p>所以核心的逻辑变成了<code>Class</code>中的 <code>methods</code>数组何时被初始化赋值的？我们刨根问底的追踪下。</p>
<p>我们先找到类加载到虚拟机中的入口方法 <code>loadNonArrayClass</code>：</p>
<pre><code>Class * loadNonArrayClass(ClassLoader * classLoader, const char * className)
&#123;
    int32_t classSize = 0;
    char * classContent = NULL;
    Class * loadClass = NULL;
    classSize = readClass(className, &amp;classContent);
    if (classSize &gt; 0 &amp;&amp; classContent != NULL)&#123;
#if 0
        printf(&quot;class size:%d,class data:[&quot;, classSize);
        for (int32_t i = 0; i &lt; classSize; i++)
        &#123;
            printf(&quot;0x%02x &quot;, classContent[i]);
        &#125;
        printf(&quot;]\n&quot;);
#endif
    &#125;
    if (classSize &lt;= 0)
    &#123;
        printf(&quot;Could not found target class\n&quot;);
        exit(127);
    &#125;
    // 解析字节码文件
    loadClass = parseClassFile(classContent, classSize);
    loadClass-&gt;classLoader = classLoader;
    // 加载
    defineClass(classLoader, loadClass);
    // 链接
    linkClass(classLoader, loadClass);

    //printf(&quot;[Loaded %s\n&quot;, loadClass-&gt;name);
    return loadClass;
&#125;

</code></pre>
<p>在 <code>parseClassFile</code>方法中，调用了<code>newClass</code>方法。</p>
<pre><code>Class * parseClassFile(char * classContent, int32_t classSize)
&#123;
    ClassFile * classFile = NULL;

    classFile = parseClassData(classContent, classSize);

    return newClass(classFile);
&#125;
</code></pre>
<p><code>newClass</code>方法在<code>rtda/heap/class.c</code>文件中。</p>
<pre><code>Class * newClass(ClassFile * classFile)
&#123;
    Class * c = calloc(1, sizeof(Class));
    c-&gt;accessFlags = classFile-&gt;accessFlags;
    c-&gt;sourceFile = getClassSourceFileName(classFile);
    newClassName(c, classFile);
    newSuperClassName(c, classFile);
    newInterfacesName(c, classFile);
    newConstantPool(c, classFile);
    newFields(c, classFile);
    newMethods(c, classFile);
    return c;

&#125;
</code></pre>
<p>可以看到，在native层创建了一个<code>Class</code>对象，我们重点看<code>newMethods(c, classFile)</code>方法啊，这个方法定义在<code>rtda/heap/method.c</code>中。</p>
<pre><code>Method * newMethods(struct Class * c, ClassFile * classFile)
&#123;
    c-&gt;methodsCount = classFile-&gt;methodsCount;
    c-&gt;methods = NULL;
    if (c-&gt;methodsCount == 0)
        return NULL;

    c-&gt;methods = calloc(classFile-&gt;methodsCount, sizeof(Method));
    for (uint16_t i = 0; i &lt; c-&gt;methodsCount; i++)
    &#123;        
        c-&gt;methods[i].classMember.attachClass = c;
        copyMethodInfo(&amp;c-&gt;methods[i], &amp;classFile-&gt;methods[i], classFile);
        copyAttributes(&amp;c-&gt;methods[i], &amp;classFile-&gt;methods[i], classFile);
        MethodDescriptor * md = parseMethodDescriptor(c-&gt;methods[i].classMember.descriptor);
        c-&gt;methods[i].parsedDescriptor = md;
        calcArgSlotCount(&amp;c-&gt;methods[i]);
        if (isMethodNative(&amp;c-&gt;methods[i]))
        &#123;
            injectCodeAttribute(&amp;c-&gt;methods[i], md-&gt;returnType);
        &#125;
    &#125;

    return NULL;
&#125;
</code></pre>
<p>上述代码可以看出，实际上就是把<code>ClassFile</code>中解析到的方法逐一赋值给了 <code>Class</code> 对象的 <code>methods</code> 数组。</p>
<p>总算梳理清楚了，反射创建对象的调用链为：</p>
<pre><code>loadClass -&gt; loadNonArrayClass -&gt; parseClassFile -&gt; newMethods -&gt; Class 的 methods数组

privateGetDeclaredConstructors -&gt; getDeclaredConstructors0 -&gt; getClassConstructors (过滤Class 的 methods数组)
</code></pre>
<p>到目前为止，我们搞明白反射时如何找到对应的构造方法的。下面我们来看 <code>newInstance</code> 方法。</p>
<pre><code>(InnerClass) clazz.getDeclaredConstructor().newInstance();

public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    &#123;
        // 构造方法是否被重载了
        if (!override) &#123;
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) &#123;
                Class&lt;?&gt; caller = Reflection.getCallerClass();
                // 检查权限
                checkAccess(caller, clazz, null, modifiers);
            &#125;
        &#125;
        // 枚举类型报错
        if ((clazz.getModifiers() &amp; Modifier.ENUM) != 0)
            throw new IllegalArgumentException(&quot;Cannot reflectively create enum objects&quot;);
        // ConstructorAccessor 是缓存的，如果为空，就去创建一个
        ConstructorAccessor ca = constructorAccessor;   // read volatile
        if (ca == null) &#123;
            // 创建 ConstructorAccessor
            ca = acquireConstructorAccessor();
        &#125;
        @SuppressWarnings(&quot;unchecked&quot;)
        // 使用 ConstructorAccessor 的 newInstance 构造实例
        T inst = (T) ca.newInstance(initargs);
        return inst;
    &#125;
</code></pre>
<p>接着看下 <code>acquireConstructorAccessor</code> 方法。</p>
<pre><code>private ConstructorAccessor acquireConstructorAccessor() &#123;
    // First check to see if one has been created yet, and take it
    // if so.
    ConstructorAccessor tmp = null;
    // 可以理解为缓存的对象
    if (root != null) tmp = root.getConstructorAccessor();
    if (tmp != null) &#123;
        constructorAccessor = tmp;
    &#125; else &#123;
        // Otherwise fabricate one and propagate it up to the root
        // 生成一个 ConstructorAccessor，并缓存起来
        tmp = reflectionFactory.newConstructorAccessor(this);
        setConstructorAccessor(tmp);
    &#125;

    return tmp;
&#125;
</code></pre>
<p>继续走到<code>newConstructorAccessor</code>方法。</p>
<pre><code>public ConstructorAccessor newConstructorAccessor(Constructor&lt;?&gt; var1) &#123;
        checkInitted();
        Class var2 = var1.getDeclaringClass();
    // 如果是抽象类，报错
    if (Modifier.isAbstract(var2.getModifiers())) &#123;
        return new InstantiationExceptionConstructorAccessorImpl((String)null);
    &#125; 
    // 如果 Class 类报错
    else if (var2 == Class.class) &#123;
        return new InstantiationExceptionConstructorAccessorImpl(&quot;Can not instantiate java.lang.Class&quot;);
    &#125; 
    // 如果是 ConstructorAccessorImpl 的子类的话，返回 BootstrapConstructorAccessorImpl 
    else if (Reflection.isSubclassOf(var2, ConstructorAccessorImpl.class)) &#123;
        return new BootstrapConstructorAccessorImpl(var1);
    &#125; 
    // 判断 noInflation , 后面是判断不是匿名类
    else if (noInflation &amp;&amp; !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) &#123;
        return (new MethodAccessorGenerator()).generateConstructor(var1.getDeclaringClass(), var1.getParameterTypes(), var1.getExceptionTypes(), var1.getModifiers());
    &#125; 
    // 使用 NativeConstructorAccessorImpl 来生成实例
    else &#123;
        NativeConstructorAccessorImpl var3 = new NativeConstructorAccessorImpl(var1);
        DelegatingConstructorAccessorImpl var4 = new DelegatingConstructorAccessorImpl(var3);
        var3.setParent(var4);
        return var4;
    &#125;
&#125;
</code></pre>
<p>具体逻辑，在上述代码中已经注释了。这里提一下 <code>noInflation</code>。</p>
<p><code>ReflectionFactory</code>在执行所有方法前会检查下是否执行过了<code>checkInitted</code>方法，这个方法会把<code>noInflation</code>的值和<code>inflationThreshold</code>从虚拟机的环境变量中读取出来并赋值。</p>
<p>当<code>noInflation</code> 为 <code>false</code>而且不是匿名类时，就会使用<code>MethodAccessorGenerator</code>方式。否则就是用 <code>NativeConstructorAccessorImpl</code>的方式来生成。</p>
<p>默认<code>noInflation</code> 为<code>false</code>，所以我们先看native调用的方式。关注 <code>NativeConstructorAccessorImpl</code>类。</p>
<pre><code>class NativeConstructorAccessorImpl extends ConstructorAccessorImpl &#123;
    private final Constructor&lt;?&gt; c;
    private DelegatingConstructorAccessorImpl parent;
    private int numInvocations;

    NativeConstructorAccessorImpl(Constructor&lt;?&gt; var1) &#123;
        this.c = var1;
    &#125;

    public Object newInstance(Object[] var1) throws InstantiationException, IllegalArgumentException, InvocationTargetException &#123;
        if (++this.numInvocations &gt; ReflectionFactory.inflationThreshold() &amp;&amp; !ReflectUtil.isVMAnonymousClass(this.c.getDeclaringClass())) &#123;
            ConstructorAccessorImpl var2 = (ConstructorAccessorImpl)(new MethodAccessorGenerator()).generateConstructor(this.c.getDeclaringClass(), this.c.getParameterTypes(), this.c.getExceptionTypes(), this.c.getModifiers());
            this.parent.setDelegate(var2);
        &#125;

        return newInstance0(this.c, var1);
    &#125;

    void setParent(DelegatingConstructorAccessorImpl var1) &#123;
        this.parent = var1;
    &#125;

    private static native Object newInstance0(Constructor&lt;?&gt; var0, Object[] var1) throws InstantiationException, IllegalArgumentException, InvocationTargetException;
&#125;

</code></pre>
<p>我们可以看到 <code>NativeConstructorAccessorImpl</code> 中维护了一个计数器<code>numInvocations</code>,在每次调用<code>newInstance</code>方法生成实例时，就会对计数器自增，当计数器超过<code>ReflectionFactory.inflationThreshold()</code>的阈值，默认为15，就会使用 <code>ConstructorAccessorImpl</code>替换 <code>NativeConstructorAccessorImpl</code>,后面就会直接调用<code>MethodAccessorGenerator</code>中的方法了。</p>
<p>我们先看看没到达阈值前，会调用native方法 <code>newInstance0</code>,这个方法定义在<code>native/sun/reflect/NativeConstructorAccessorImpl.c</code>中，具体<code>newInstance0</code>的流程我就不分析了，大致逻辑是操作堆栈执行方法。</p>
<p>然后我们再看看超过阈值后，执行的是 <code>MethodAccessorGenerator</code>生成构造器的方式。这种方式与<code>newConstructorAccessor</code>方法中<code>noInflation</code> 为 <code>false</code>的处理方式一样。所以可以解释为：java虚拟机在执行反射操作时，如果同一操作执行次数超过阈值，会从native生成实例的方式转变为java生成实例的方式。</p>
<p><code>MethodAccessorGenerator</code>的<code>MethodAccessorGenerator</code>方法如下。</p>
<pre><code>public ConstructorAccessor generateConstructor(Class&lt;?&gt; var1, Class&lt;?&gt;[] var2, Class&lt;?&gt;[] var3, int var4) &#123;
    return (ConstructorAccessor)this.generate(var1, &quot;&lt;init&gt;&quot;, var2, Void.TYPE, var3, var4, true, false, (Class)null);
&#125;
</code></pre>
<p>继续跟踪下去可以发现，反射调用构造方法实际上是动态编写字节码，并且在虚拟机中把编好的字节码加载成一个Class，这个Class实际上是 <code>ConstructorAccessorImpl</code> 类型的，然后调用这个动态类的<code>newInstance</code>方法。回看刚刚我们梳理的<code>newConstructorAccessor</code>代码，可以看到第三个逻辑：</p>
<pre><code>// 如果是 ConstructorAccessorImpl 的子类的话，返回 BootstrapConstructorAccessorImpl 
else if (Reflection.isSubclassOf(var2, ConstructorAccessorImpl.class)) &#123;
    return new BootstrapConstructorAccessorImpl(var1);
&#125; 
</code></pre>
<p>最终执行的是 <code>BootstrapConstructorAccessorImpl</code>的<code>newInstance</code>方法。</p>
<pre><code>class BootstrapConstructorAccessorImpl extends ConstructorAccessorImpl &#123;
    private final Constructor&lt;?&gt; constructor;

    BootstrapConstructorAccessorImpl(Constructor&lt;?&gt; var1) &#123;
        this.constructor = var1;
    &#125;

    public Object newInstance(Object[] var1) throws IllegalArgumentException, InvocationTargetException &#123;
        try &#123;
            return UnsafeFieldAccessorImpl.unsafe.allocateInstance(this.constructor.getDeclaringClass());
        &#125; catch (InstantiationException var3) &#123;
            throw new InvocationTargetException(var3);
        &#125;
    &#125;
&#125;
</code></pre>
<p>最后是通过使用<code>Unsafe</code>类分配了一个实例。</p>
<h2 id="反射带来的问题"><a href="#反射带来的问题" class="headerlink" title="反射带来的问题"></a>反射带来的问题</h2><p>到现在为止，我们已经把反射生成实例的所有流程都搞清楚了。回到文章开头的问题，我们现在反思下，反射性能低么？为什么？</p>
<ol>
<li>反射调用过程中会产生大量的临时对象，这些对象会占用内存，可能会导致频繁 gc，从而影响性能。</li>
<li>反射调用方法时会从方法数组中遍历查找，并且会检查可见性等操作会耗时。</li>
<li>反射在达到一定次数时，会动态编写字节码并加载到内存中，这个字节码没有经过编译器优化，也不能享受JIT优化。</li>
<li>反射一般会涉及自动装箱/拆箱和类型转换，都会带来一定的资源开销。</li>
</ol>
<p>在Android中，我们可以在某些情况下对反射进行优化。举个例子，EventBus 2.x 会在 register 方法运行时，遍历所有方法找到回调方法；而EventBus 3.x 则在编译期间，将所有回调方法的信息保存的自己定义的 <code>SubscriberMethodInfo</code> 中，这样可以减少对运行时的性能影响。</p>
<p><strong>本文的结论如下：</strong></p>
<ol>
<li>不要在性能敏感的应用中，频繁调用反射。</li>
<li>如果反射执行的次数小于1000这个数量级，反射的耗时实际上与正常调用无太大差异。</li>
<li>反射对内存占用还有一定影响的，在内存敏感的场景下，谨慎使用反射。</li>
</ol>

        </div>

    </div>

    

    

    

    

    

    
<nav class="article-nav">
  
    <a href="/2020/06/12/Android%E7%9B%AE%E5%89%8D%E6%9C%80%E7%A8%B3%E5%AE%9A%E5%92%8C%E9%AB%98%E6%95%88%E7%9A%84UI%E9%80%82%E9%85%8D%E6%96%B9%E6%A1%88/" id="article-nav-newer" class="article-nav-link-wrap">
      <div class="article-nav-caption">下一篇</div>
      <div class="article-nav-title">
        
          Android目前最稳定和高效的UI适配方案
        
      </div>
    </a>
  
  
    <a href="/2020/06/12/HashMap%E5%8E%9F%E7%90%86%E6%8A%80%E6%9C%AF%E7%9F%A5%E8%AF%86%E6%95%B4%E7%90%86/" id="article-nav-older" class="article-nav-link-wrap">
      <div class="article-nav-caption">上一篇</div>
      <div class="article-nav-title">HashMap原理技术知识整理</div>
    </a>
  
</nav>


    <section class="share">
        <div class="share-title">分享</div>
        <a class="share-item" target="_blank"
            href="https://twitter.com/share?text=Java反射真的很慢吗 - ApocalypseBlog&url=http://example.com/2020/06/12/Java%E5%8F%8D%E5%B0%84%E7%9C%9F%E7%9A%84%E5%BE%88%E6%85%A2%E5%90%97/">
            <box-icon type='logo' name='twitter'></box-icon>
        </a>
        <a class="share-item" target="_blank"
            href="https://www.facebook.com/sharer.php?title=Java反射真的很慢吗 - ApocalypseBlog&u=http://example.com/2020/06/12/Java%E5%8F%8D%E5%B0%84%E7%9C%9F%E7%9A%84%E5%BE%88%E6%85%A2%E5%90%97/">
            <box-icon name='facebook-square' type='logo' ></box-icon>
        </a>
        <!-- <a class="share-item" target="_blank"
            href="https://service.weibo.com/share/share.php?title=Java反射真的很慢吗 - ApocalypseBlog&url=http://example.com/2020/06/12/Java%E5%8F%8D%E5%B0%84%E7%9C%9F%E7%9A%84%E5%BE%88%E6%85%A2%E5%90%97/&pic=">
            <div class="n-icon n-icon-weibo"></div>
        </a> -->
    </section>

</article>









</div>
                </section>
            </section>

             
            <aside class="sidebar">
            
                
            </aside>
        </div>
    </div>

    <footer class="footer">
    <div class="footer-wave">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 320"><path fill="#3c4859" fill-opacity="1" d="M0,160L60,181.3C120,203,240,245,360,240C480,235,600,181,720,186.7C840,192,960,256,1080,261.3C1200,267,1320,213,1380,186.7L1440,160L1440,320L1380,320C1320,320,1200,320,1080,320C960,320,840,320,720,320C600,320,480,320,360,320C240,320,120,320,60,320L0,320Z"></path></svg>
    </div>

    <div class="footer-wrap">
        <div class="footer-inner"> 
            ApocalypseBlog &copy; 2021<br>
            Powered By Hexo · Theme By <a href="https://github.com/lh1me/hexo-theme-aomori" target="_blank">Aomori</a>
        </div>
    </div>

</footer>




<script src="/dist/build.js?1.14.0.js"></script>


<script src="/dist/custom.js?1.14.0.js"></script>









</body>

</html>